Contents¶
- Flight Logs
- 3D Geometry
- State Representation
- Model and Environment
- Telemetry
Flight Logs¶
- Generally nasty binary formats
- pymavlink provides a parser for Ardupilot Logs
- pip install flightdata
- Parses and handles flight data with a standard interface and units
- Dumps to csv for faster reading in future
In [3]:
from flightdata import Flight
fl = Flight.from_csv("montserrat.csv")
fl.data
Out[3]:
| time_index | servos_6 | servos_7 | axis_rate_roll | tx_controls_3 | servos_1 | tx_controls_4 | servos_4 | magnetometer_0 | time_actual | ... | quaternion_1 | rpm_0 | battery_1 | altitude_baro | current_1 | altitude_gps | quaternion_3 | rpm_1 | quaternion_0 | quaternion_2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| time_index | |||||||||||||||||||||
| 0.000000 | 0.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 230.034444 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 0.039956 | 0.039956 | 1000.0 | 1500.0 | 0.001441 | 1498.0 | 1431.0 | 987.0 | 1472.0 | -324.0 | 230.074400 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 0.079985 | 0.079985 | 1000.0 | 1500.0 | 0.003569 | 1498.0 | 1431.0 | 987.0 | 1472.0 | -324.0 | 230.114429 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 0.120053 | 0.120053 | 1000.0 | 1500.0 | 0.009420 | 1498.0 | 1431.0 | 987.0 | 1472.0 | -324.0 | 230.154497 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 0.159986 | 0.159986 | 1000.0 | 1500.0 | 0.006934 | 1498.0 | 1431.0 | 987.0 | 1472.0 | -319.0 | 230.194430 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1539.599988 | 1539.599988 | 1000.0 | 1500.0 | 0.010239 | 1500.0 | 1428.0 | 987.0 | 1472.0 | -258.0 | 1769.634432 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1539.640014 | 1539.640014 | 1000.0 | 1500.0 | -0.000365 | 1500.0 | 1428.0 | 987.0 | 1472.0 | -269.0 | 1769.674458 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1539.680044 | 1539.680044 | 1000.0 | 1500.0 | -0.002612 | 1498.0 | 1428.0 | 987.0 | 1472.0 | -269.0 | 1769.714488 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1539.719949 | 1539.719949 | 1000.0 | 1500.0 | 0.003439 | 1500.0 | 1428.0 | 987.0 | 1472.0 | -269.0 | 1769.754393 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1539.759993 | 1539.759993 | 1000.0 | 1500.0 | 0.001994 | 1498.0 | 1429.0 | 987.0 | 1472.0 | -270.0 | 1769.794437 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
38495 rows × 69 columns
In [4]:
fl.global_position
Out[4]:
| global_position_latitude | global_position_longitude | |
|---|---|---|
| time_index | ||
| 0.000000 | NaN | NaN |
| 0.039956 | NaN | NaN |
| 0.079985 | 16.714695 | -62.227804 |
| 0.120053 | 16.714695 | -62.227804 |
| 0.159986 | 16.714695 | -62.227804 |
| ... | ... | ... |
| 1539.599988 | 16.714697 | -62.227885 |
| 1539.640014 | 16.714697 | -62.227885 |
| 1539.680044 | 16.714697 | -62.227885 |
| 1539.719949 | 16.714697 | -62.227885 |
| 1539.759993 | 16.714697 | -62.227885 |
38495 rows × 2 columns
In [5]:
fl.servos
Out[5]:
| servos_0 | servos_1 | servos_2 | servos_3 | servos_4 | servos_5 | servos_6 | servos_7 | servos_8 | servos_9 | servos_10 | servos_11 | servos_12 | servos_13 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| time_index | ||||||||||||||
| 0.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 0.039956 | 1517.0 | 1431.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 1861.0 | 1160.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 0.079985 | 1517.0 | 1431.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 1861.0 | 1160.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 0.120053 | 1517.0 | 1431.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 1861.0 | 1160.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 0.159986 | 1517.0 | 1431.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 1861.0 | 1160.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1539.599988 | 1517.0 | 1428.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 2031.0 | 956.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 1539.640014 | 1517.0 | 1428.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 2031.0 | 956.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 1539.680044 | 1517.0 | 1428.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 2031.0 | 956.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 1539.719949 | 1517.0 | 1428.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 2031.0 | 956.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 1539.759993 | 1517.0 | 1429.0 | 1000.0 | 1565.0 | 1472.0 | 0.0 | 1000.0 | 1500.0 | 2031.0 | 956.0 | 0.0 | 0.0 | 0.0 | 0.0 |
38495 rows × 14 columns
In [6]:
px.scatter_geo(lat=fl.global_position.iloc[:,0], lon=fl.global_position.iloc[:,1])
3D Geometry¶
- pip install pfc-geometry
- Nice interface to geometric entities
- Based on numpy
- Objects to handle arrays of Point, Quaternion, GPS, Coord, Transformation
- Extensive use of magic methods
- built on 2D numpy arrays
In [7]:
from geometry import *
Point(1,2,3).data
Out[7]:
array([[1, 2, 3]])
Basic Geometry Constructors and Operations¶
In [8]:
plotpoints(2,
constructors = [
Point(1, 2, 0), PX(5), P0(),
Point(np.random.random((100,3))*-5 -5),
],
operations = [
Point(1,1,0) * np.linspace(0,10,10),
PX(1) * np.linspace(0,5,4) * 2,
PX(-1,10).cumsum()
]
)
More Geometric Operations¶
In [9]:
from flightplotting import *
origin = Coord.zero()
transformed = Transformation(Point(10,10,10), Euldeg(45, 45, 45)).apply(origin)
create_3d_plot(axestrace([origin, transformed], 5))
State Representation¶
- Handles time, Position, Attitude and their derivatives
- wraps a pandas dataframe
- Made up of groups of geometric entities
- Constructored from flight data or from scratch
- pip install flightanalysis
In [10]:
from flightanalysis import *
st = State.from_flight(fl[fl.imu_ready_time():])
st.data
Out[10]:
| t | dt | x | y | z | rw | rx | ry | rz | u | ... | w | p | q | r | du | dv | dw | dp | dq | dr | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0.000000 | 1.684872e+09 | 0.040068 | -2.531698 | -1.774133 | -12.398801 | 0.006849 | 0.707074 | 0.707100 | -0.003147 | -0.006290 | ... | -0.046262 | 0.003569 | -0.005179 | -0.030917 | 0.490665 | 0.652766 | -9.562064 | 0.146012 | 0.124210 | 0.115515 |
| 0.040068 | 1.684872e+09 | 0.040001 | -2.532421 | -1.774749 | -12.402140 | 0.006849 | 0.707074 | 0.707100 | -0.003147 | -0.006619 | ... | -0.046038 | 0.009420 | -0.000202 | -0.026289 | 0.355979 | 0.597122 | -9.423726 | 0.042064 | 0.051102 | -0.002953 |
| 0.080001 | 1.684872e+09 | 0.039993 | -2.533040 | -1.775352 | -12.404810 | 0.006788 | 0.707074 | 0.707100 | -0.003209 | -0.007012 | ... | -0.045611 | 0.006934 | -0.001090 | -0.031154 | 0.443138 | 0.579210 | -9.312488 | -0.056727 | -0.057763 | -0.050796 |
| 0.120054 | 1.684872e+09 | 0.039983 | -2.533577 | -1.775947 | -12.406843 | 0.006726 | 0.707075 | 0.707099 | -0.003270 | -0.007376 | ... | -0.045472 | 0.004882 | -0.004822 | -0.030352 | 0.447935 | 0.565713 | -9.385395 | -0.113635 | -0.074322 | 0.003750 |
| 0.159967 | 1.684872e+09 | 0.039979 | -2.534048 | -1.776527 | -12.408628 | 0.006726 | 0.707075 | 0.707099 | -0.003270 | -0.007756 | ... | -0.045771 | -0.002153 | -0.007034 | -0.030854 | 0.434582 | 0.589533 | -9.457307 | -0.076899 | 0.058784 | -0.003136 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1539.520003 | 1.684873e+09 | 0.040032 | 4.086720 | -7.192983 | -7.675329 | -0.026632 | 0.761554 | 0.644811 | -0.059542 | 0.055480 | ... | 0.084307 | 0.010239 | -0.000310 | -0.011462 | 0.320623 | -0.616758 | -9.750603 | -0.030198 | 0.011543 | 0.084326 |
| 1539.560029 | 1.684873e+09 | 0.040028 | 4.086779 | -7.192683 | -7.677584 | -0.026632 | 0.761554 | 0.644811 | -0.059542 | 0.056572 | ... | 0.084107 | -0.000365 | 0.000066 | -0.003348 | 0.281426 | -0.544395 | -9.751322 | -0.160521 | -0.030017 | -0.002480 |
| 1539.600059 | 1.684873e+09 | 0.039968 | 4.086976 | -7.192109 | -7.680097 | -0.026632 | 0.761554 | 0.644811 | -0.059542 | 0.056988 | ... | 0.085211 | -0.002612 | -0.002713 | -0.011661 | 0.309695 | -0.487353 | -9.747066 | 0.047594 | 0.017272 | -0.045476 |
| 1539.639964 | 1.684873e+09 | 0.039974 | 4.087298 | -7.191294 | -7.683197 | -0.026632 | 0.761554 | 0.644811 | -0.059542 | 0.058266 | ... | 0.085886 | 0.003439 | 0.001446 | -0.006983 | 0.278705 | -0.579164 | -9.771101 | 0.057608 | 0.009559 | 0.031185 |
| 1539.680008 | 1.684873e+09 | 0.040044 | 4.087129 | -7.192113 | -7.684982 | -0.026632 | 0.761554 | 0.644811 | -0.059542 | 0.057955 | ... | 0.086363 | 0.001994 | -0.001949 | -0.009168 | 0.310242 | -0.579635 | -9.474611 | -0.036103 | -0.084786 | -0.054542 |
38493 rows × 21 columns
In [11]:
pd.DataFrame([[k, v.obj.__name__, v.keys] for k, v in State.constructs.data.items()], columns=['name', 'object', 'cols'])
Out[11]:
| name | object | cols | |
|---|---|---|---|
| 0 | time | Time | [t, dt] |
| 1 | pos | Point | [x, y, z] |
| 2 | att | Quaternion | [rw, rx, ry, rz] |
| 3 | vel | Point | [u, v, w] |
| 4 | rvel | Point | [p, q, r] |
| 5 | acc | Point | [du, dv, dw] |
| 6 | racc | Point | [dp, dq, dr] |
In [12]:
st.time
Out[12]:
Time(t_=1684872557.28 dt_=0.04, len=38493)
In [13]:
st.pos
Out[13]:
Point(x_=-1503.97 y_=1680.0 z_=436.41, len=38493)
In [14]:
st.att
Out[14]:
Quaternion(w_=-0.04 x_=0.41 y_=0.63 z_=-0.04, len=38493)
In [15]:
plotsec(st)
In [16]:
plotsec(st[135:142], nmodels=20, scale=2)
Generating Trajectories¶
In [17]:
track = State.from_transform(
Transformation(P0(), Euldeg(180,0,0)),
vel=PX(20), rvel=np.pi * Point(0.25, 0.25, 0)
).extrapolate(6)
fig = plotsec(track, scale=2, nmodels=10)
fig.show()
Displaying Quantities¶
In [18]:
fig.add_traces(vectors(20, track, 0.5*track.body_to_world(track.acc, True)))
fig.add_traces(vectors(20, track, 5*track.body_to_world(track.rvel, True), line=dict(color="green")))
fig.show()
Model and Environment¶
- Add some more State type datastructures
- Environment
- atmosphere
- wind
- Flow
- alpha & beta
- q
- Coefficients
- Forces
- Moments
- Environment
- Define some constants
- S (wing area), c (chord), b (span), mass (mass and inertia properties)
Add some wind and rotate to the Wind axis¶
In [19]:
env = Environment.from_constructs(Time.now(), wind=PY(5))
wind = track.track_to_wind(env)
plotsec([track, wind], nmodels=10, scale=3)
Calculate the Coefficients, Alpha and Beta¶
In [20]:
from flightanalysis.model import cold_draft as constants
flow = Flow.build(wind, env)
coeffs = Coefficients.build(wind, flow.q, constants)
flow = flow.rotate(coeffs, 10, 5) # Assume linear dcl/da and dcydb.
px.line(np.degrees(flow.flow.to_pandas().iloc[:,:-1])).show()
Rotate by Alpha and Beta to get Body Axis¶
In [21]:
body = wind.wind_to_body(flow)
plotsec([track, wind, body], nmodels=10, scale=3)
Telemetry¶
- Most drones communicate via MAVLink (pip install pymavlink)
- dronekit wraps pymavlink to provide a higher level api
- My alternative to dronekit: github.com/PyFlightCoach/DroneInterface
In [22]:
%%script false --no-raise-error
vehicle = Vehicle.connect('tcp:127.0.0.1:5760', 1, 1, "log_tmp")
#getting a single mavlink message
vehicle.get_GlobalOrigin(5, None).position # -> GPS
#getting a state object (combines 3 mavlink messages):
vehicle.get_state()
#subscribing to a higher rate for some messages
with vehicle.subscribe(vehicle.state.ids, 10):
last_state() # gets the last
next_state() # blocks till the next
Summary¶
- Sources:
- github.com/PyFlightCoach
- pypi - flightdata, pfc-geometry, flightplotting, flightanalysis
- docker pull thomasdavid/pyflightcoach:latest
- github.com/PyFlightCoach/examples_presentation